
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2023/05/01
// Design Name:
// Module Name: rm_ahb_if
// Project Name: wujian100
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// base ahb addr is 0x4001_0000  none offset
//////////////////////////////////////////////////////////////////////////////////
//r2t_mailbox_top模块状态机个数
`define R2TM_NUM        7

//定义hresp信号
`define OKAY            2'b00
`define ERROR           2'b01
`define RETRY           2'b10
`define SPLIT           2'b11

module rm_ahb_if
   #(
    parameter ADDR_W                   = 12,
    parameter MODE_SEL                 = 0                   
    )
    (
    input                              rai_clk_i,
    input                              rai_rst_n_i,
    input                 [32 - 1 : 0] rai_haddr_i,
    input                 [4  - 1 : 0] rai_hprot_i,                            //hprot[0]为0，代表是来自tee的访问信号，为1，代表是来自ree的访问信号
    input                 [1  - 1 : 0] rai_hsel_i,
    input                 [3  - 1 : 0] rai_hsize_i,
    input                 [2  - 1 : 0] rai_htrans_i,
    input                 [32 - 1 : 0] rai_hwdata_i,
    input                 [1  - 1 : 0] rai_hwrite_i,

    output                [32 - 1 : 0] rai_hrdata_o,
    output                [1  - 1 : 0] rai_hready_o,
    output                [2  - 1 : 0] rai_hresp_o,
    output                [1  - 1 : 0] rai_r2t_intr_o,

    output                [1  - 1 : 0] r2t_clk_w,
    output                [1  - 1 : 0] r2t_rst_n_w,
    output                             en_box,
    output                             write_box,
    output            [ADDR_W - 1 : 0] r2t_addr_w,
    output                [32 - 1 : 0] r2t_wdat_w,
    input                 [1  - 1 : 0] r2t_rdy_w,
    input                 [32 - 1 : 0] r2t_rdat_w,
    input                 [1  - 1 : 0] r2t_intr_w
    );

wire                      [1  - 1 : 0] r2t_w_en_w;
wire                      [1  - 1 : 0] r2t_r_en_w;

//////////////////////////////////////
//本地参数                           //
//////////////////////////////////////

localparam PORT_FIELD                  = 0;                                    //标志PORT信号的第几位是有效位

localparam IDLE                        = `R2TM_NUM'b0000000;                 //空闲状态
localparam WRITE_CMD                   = `R2TM_NUM'b0000001;                 //写指令状态
localparam READ_CMD                    = `R2TM_NUM'b0000010;                 //读指令状态
localparam WITE_W_DAT                  = `R2TM_NUM'b0000100;                 //等待写数据状态
localparam WITE_R_DAT                  = `R2TM_NUM'b0001000;                 //等待读数据状态
localparam WRITE_DAT                   = `R2TM_NUM'b0010000;                 //写数据状态
localparam READ_DAT                    = `R2TM_NUM'b0100000;                 //读数据状态
localparam ERROR_RESP                  = `R2TM_NUM'b1000000;                  //违规请求响应状态

//////////////////////////////////////
//状态机信号                         //
//////////////////////////////////////

reg     [`R2TM_NUM-1:0] state_c;
reg     [`R2TM_NUM-1:0] state_n;

reg        [32 - 1 : 0] rai_haddr_r;
reg        [32 - 1 : 0] rai_hwdata_r;
reg                     rai_hready_r;
reg        [2  - 1 : 0] rai_hresp_r;
reg        [32 - 1 : 0] rai_hrdata_r;

//////////////////////////////////////
//违规请求标志信号                    //
//////////////////////////////////////

wire                    error_req_flag_w;                                      //错误请求标志线

//////////////////////////////////////
//读写使能信号                       //
//////////////////////////////////////

wire                    w_en;                                                  //写使能标志
wire                    r_en;                                                  //读使能标志
wire                    en;                                                    //使能信号
wire                    handshake;                                             //握手信号


/************************************/
/*系统信号                           */
/************************************/

assign r2t_clk_w        = rai_clk_i;
assign r2t_rst_n_w      = rai_rst_n_i;

/************************************/
/*读写使能信号                       */
/************************************/
assign handshake        = ((rai_hsel_i == 1'b1)&&
                           (rai_hready_o == 1'b1)) ? 1'b1 : 1'b0;

assign w_en             = ((handshake == 1'b1)&&
                           (rai_hwrite_i == 1'b1)&&
                           (rai_htrans_i[1] == 1'b1)&&
                           (error_req_flag_w == 1'b0)) ? 1'b1 : 1'b0;

assign r_en             = ((handshake == 1'b1)&&
                           (rai_hwrite_i == 1'b0)&&
                           (rai_htrans_i[1] == 1'b1)&&
                           (error_req_flag_w == 1'b0)) ? 1'b1 : 1'b0;

assign en               = ((w_en == 1'b1)||(r_en == 1'b1)) ? 1'b1 : 1'b0;

/************************************/
/*违规请求标志                       */
/************************************/
                                                                                         //如果访问违规，则产生违规标志
// assign error_req_flag_w = ((((rai_hprot_i[PORT_FIELD] == 1'b1)&&(rai_hwrite_i == 1'b1))||//为来自ree的请求，且是写请求
//                            ((rai_hprot_i[PORT_FIELD] == 1'b0)&&(rai_hwrite_i == 1'b0)))&&//为来自tee的请求，且为读请求
//                            (rai_hsel_i == 1'b1)&&(state_c == IDLE)) ? 1'b0 : 1'b1;       //选择信号为高，代表这些信号有效，上述表达式为真，则标志信号为低电平

generate

if (MODE_SEL == 0)                                                             //小信箱
assign error_req_flag_w = ((((rai_hprot_i[PORT_FIELD] == 1'b0)&&(rai_hwrite_i == 1'b1)))&&//为来自tee的请求，且为写请求，属于违规操作
                           (rai_hsel_i == 1'b1)&&(state_c == IDLE)) ? 1'b1 : 1'b0;       //选择信号为高，代表这些信号有效，上述表达式为真
else if (MODE_SEL == 1)                                                        //大信箱读写端
assign error_req_flag_w = ((((rai_hprot_i[PORT_FIELD] == 1'b0)))&&             //该通道为ree专用通道，只要是来自tee的访问都属于违规访问
                           (rai_hsel_i == 1'b1)&&(state_c == IDLE)) ? 1'b1 : 1'b0;       //选择信号为高，代表这些信号有效，上述表达式为真
else if (MODE_SEL == 2)                                                        //大信箱只读端
assign error_req_flag_w = ((((rai_hprot_i[PORT_FIELD] == 1'b0)&&(rai_hwrite_i == 1'b1)))&&//为来自tee的请求，且为写请求，属于违规操作
                           (((rai_hprot_i[PORT_FIELD] == 1'b1)))&&             //该通道为tee专用通道，只要是来自ree的访问都属于违规访问
                           (rai_hsel_i == 1'b1)&&(state_c == IDLE)) ? 1'b1 : 1'b0;       //选择信号为高，代表这些信号有效，上述表达式为真
else                                                                           //默认情况
assign error_req_flag_w = ((((rai_hprot_i[PORT_FIELD] == 1'b0)&&(rai_hwrite_i == 1'b1)))&&//为来自tee的请求，且为写请求，属于违规操作
                           (rai_hsel_i == 1'b1)&&(state_c == IDLE)) ? 1'b1 : 1'b0;       //选择信号为高，代表这些信号有效，上述表达式为真
endgenerate
// //hold住违规请求标志
// always@(posedge rai_clk_i or negedge rai_rst_n_i)
// begin : ERROR_REQ_FLAG_R_PROG
//   if (rai_rst_n_i == 1'b0)
//     error_req_flag_r    <= 1'b0;
//   else
//     error_req_flag_r    <= error_req_flag_w;
// end

/************************************/
/*状态机                            */
/************************************/

/*************part 1*************/
always@(posedge rai_clk_i or negedge rai_rst_n_i)                              //当前状态
begin : STATE_C_PROG
  if (rai_rst_n_i == 1'b0)
    state_c             <= {(`R2TM_NUM){1'b0}};
  else
    state_c             <= state_n;
end

/*************part 2*************/
always@(*)                                                                     //状态转换
begin : STATE_N_PROG
  case (state_c)
	IDLE        :
    begin
	  if (error_req_flag_w == 1'b1)                                            //如果出现违规请求
	    state_n         = ERROR_RESP;                                          //转换到错误响应状态
	  else if (w_en == 1'b1)                                                   //如果写使能
	    state_n         = WRITE_CMD;                                           //转换到写命令状态
	  else if (r_en == 1'b1)                                                   //如果读使能
	    state_n         = READ_CMD;                                            //转换到读命令状态
	  else                                                                     //其它情况
	    state_n         = IDLE;                                                //空闲状态
	end
    WRITE_CMD   :
	begin
	  if (r2t_rdy_w == 1'b0)                                                   //如果从机没有准备好
	    state_n         = WITE_W_DAT;                                          //则等待写数据
	  else if (r2t_rdy_w == 1'b1)                                              //如果从机准备好
	    state_n         = WRITE_DAT;                                           //写数据
	  else                                                                     //其它情况
	    state_n         = ERROR_RESP;                                          //返回错误响应
	end
    READ_CMD    :
	begin
	  if (r2t_rdy_w == 1'b0)                                                   //如果从机没有准备好
	    state_n         = WITE_R_DAT;                                          //则等待读数据
	  else if (r2t_rdy_w == 1'b1)                                              //如果从机准备好
	    state_n         = READ_DAT;                                            //读数据
	  else                                                                     //其它情况
	    state_n         = ERROR_RESP;                                          //返回错误响应
	end
    WITE_W_DAT  :
	begin
      if (r2t_rdy_w == 1'b0)                                                   //如果从机没有准备好
	    state_n         = WITE_W_DAT;                                          //则等待写数据
	  else                                                                     //从机准备好
	    state_n         = WRITE_DAT;                                           //写数据
	end
    WITE_R_DAT  :
	begin
      if (r2t_rdy_w == 1'b0)                                                   //如果从机没有准备好
	    state_n         = WITE_R_DAT;                                          //则等待读数据
	  else                                                                     //从机准备好
	    state_n         = READ_DAT;                                            //读数据
	end
    WRITE_DAT   :
    begin
      if (w_en == 1'b1)                                                        //如果在写数据的时候主机已发送下一组写指令
	    state_n         = WRITE_CMD;                                           //转换到写指令
	  else if (r_en == 1'b1)                                                   //如果在写数据的时候主机已发送下一组读指令
	    state_n         = READ_CMD;                                            //转换到读指令
	  else                                                                     //其它情况
	    state_n         = IDLE;                                                //转换到空闲状态
	end
    READ_DAT    :
    begin
      if (r_en == 1'b1)                                                        //如果在写数据的时候主机已发送下一组读指令
	    state_n         = READ_CMD;                                            //转换到读指令
	  else if (w_en == 1'b1)                                                   //如果在写数据的时候主机已发送下一组写指令
	    state_n         = WRITE_CMD;                                           //转换到写指令
	  else                                                                     //其它情况
	    state_n         = IDLE;                                                //转换到空闲状态
	end
    ERROR_RESP  :
    begin
      if (r_en == 1'b1)                                                        //如果在写数据的时候主机已发送下一组读指令
	    state_n         = READ_CMD;                                            //转换到读指令
	  else if (w_en == 1'b1)                                                   //如果在写数据的时候主机已发送下一组写指令
	    state_n         = WRITE_CMD;                                           //转换到写指令
	  else                                                                     //其它情况
	    state_n         = IDLE;                                                //转换到空闲状态
	end
	default     :
      state_n           = IDLE;
  endcase
end

/*************part 3*************/

always@(posedge rai_clk_i or negedge rai_rst_n_i)
begin : RAI_HADDR_R_PROG
  if (rai_rst_n_i == 1'b0)                                                     //复位信号有效
    rai_haddr_r         <= {(32){1'b0}};                                       //复位
  else if ((state_c == IDLE)&&(en == 1'b1))                                    //当前为空闲状态，并且有指令路由过来时
    rai_haddr_r         <= rai_haddr_i;                                        //将指令信号寄存
  else                                                                         //其它情况
    rai_haddr_r         <= rai_haddr_r;                                        //保持
end

// always@(posedge rai_clk_i or negedge rai_rst_n_i)
// begin : RAI_HWDATA_R_PROG
//   if (rai_rst_n_i == 1'b0)                                                     //复位信号有效
//     rai_hwdata_r        <= {(32){1'b0}};                                       //复位
//   else if ((state_c == WRITE_CMD)&&(state_n == WRITE_DAT)||                    //当前为写指令，并且下一个状态为写数据
//            (state_c == WITE_W_DAT)&&(state_n == WRITE_DAT))                    //当前为等待写数据指令，并且下一个状态为写数据
//     rai_hwdata_r        <= rai_hwdata_i;                                       //将指令信号寄存
//   else                                                                         //其它情况
//     rai_hwdata_r        <= rai_hwdata_r;                                       //保持
// end

always@(*)
begin : RAI_HWDATA_R_PROG
  if (state_c == WRITE_DAT)                                                    //当前为写指令，并且下一个状态为写数据
    rai_hwdata_r         = rai_hwdata_i;                                       //将指令信号寄存
  else                                                                         //其它情况
    rai_hwdata_r         = {(32){1'b0}};                                       //无效
end

always@(*)
begin : RAI_HREADY_R_PROG
  if ((state_c == IDLE)||                                                      //空闲状态
      (state_c == ERROR_RESP)||                                                //当前为错误响应状态，立即响应错误，ahb_rdy为高
	  (state_c == WRITE_DAT)||
	  (state_c == READ_DAT))
    //   ((state_c == WRITE_CMD)&&(state_n == WRITE_DAT))||                       //当前为写指令，并且下一个状态为写数据
    //   ((state_c == WITE_W_DAT)&&(state_n == WRITE_DAT))||                      //当前为等待写数据指令，并且下一个状态为写数据
	//   ((state_c == READ_CMD)&&(state_n == READ_DAT))||                         //当前为读指令，并且下一个状态为读数据
    //   ((state_c == WITE_R_DAT)&&(state_n == READ_DAT)))                        //当前为等待读数据指令，并且下一个状态为读数据
    rai_hready_r        = 1'b1;                                                //rdy有效
  else                                                                         //其它情况
    rai_hready_r        = 1'b0;                                                //rdy无效
end

always@(*)
begin : RAI_HRESP_R_PROG
  if (state_c == ERROR_RESP)                                                   //当前为响应错误状态
    rai_hresp_r         = `ERROR;                                              //响应错误
  else                                                                         //其它情况
    rai_hresp_r         = `OKAY;                                               //传输正确
end

always@(*)
begin : RAI_HRDATA_R_PROG
  if (state_c == READ_DAT)                                                     //当前为读数据状态
    rai_hrdata_r        = r2t_rdat_w;                                          //响应读数据
  else                                                                         //其它情况
    rai_hrdata_r        = {(32){1'b0}};                                        //归零
end


/************************************/
/*AHB写控制信号                      */
/************************************/

assign r2t_addr_w       = rai_haddr_r[ADDR_W-1:0];                             //由状态机控制
assign r2t_wdat_w       = rai_hwdata_r;                                        //由状态机控制
assign r2t_w_en_w       = (state_c == WRITE_DAT) ? 1'b1 : 1'b0;                //当状态机跳转到写数据状态时
assign r2t_r_en_w       = (state_c == READ_DAT) ? 1'b1 : 1'b0;                 //当状态机跳转到读数据状态时
assign en_box           = r2t_r_en_w|r2t_w_en_w;
assign write_box        = ((r2t_r_en_w == 1'b0) && (r2t_w_en_w == 1'b1)) ? 1'b1 : 1'b0;

/************************************/
/*AHB读控制信号                      */
/************************************/

//在接收到违规请求之后，下一个时钟周期返回错误响应
assign rai_hrdata_o     = rai_hrdata_r;                                        //读数据信号由状态机控制
assign rai_hresp_o      = rai_hresp_r;                                         //响应信号的生成由状态机控制，读写都需要
assign rai_hready_o     = rai_hready_r;                                        //将总线上的ready信号与从机的ready信号隔离，通过状态机单独控制

assign rai_r2t_intr_o   = r2t_intr_w;

endmodule


	// rai_clk_i     & input   & 1    & AHB总线时钟信号，时钟，上升沿数据有效。      \\ \hline
	// rai_rst_n_i   & input   & 1    & AHB总线复位信号，低有效，该信号可直接和系统总线复位连接。\\ \hline
	// rai_haddr_i   & input   & 32   & AHB总线地址信号       \\ \hline
	// rai_hprot_i   & input   & 4    & AHB总线保护控制信号，用于指示本次传输类型。      \\ \hline
	// rai_hsel_i    & input   & 1    & AHB总线Slave设备选择信号，用法和APB的PSEL类似。当Slave被选中且为非IDLE传输时，HSELx必须和地址、控制信号同一拍有效。      \\ \hline
	// rai_hsize_i   & input   & 3    & AHB总线信号，用于指示每次传输的数据大小。      \\ \hline
	// rai_htrans_i  & input   & 2    & AHB总线信号，用于指示传输类型，有四种类型：IDLE、BUSY、NONSEQUENTIAL、SEQUENTIAL，分别是空闲、忙、非连续传输、连续传输。      \\ \hline
	// rai_hwdata_i  & input   & 32   & AHB总线写数据信号，最大位宽支持1024bits，建议在32bits - 256bits之间。       \\ \hline
	// rai_hwrite_i  & input   & 1    & AHB总线读写指示信号，“1”为写，“0”为读。      \\ \hline
	// rai_hrdata_o  & output  & 32   & AHB总线读数据信号，最大位宽支持1024bits，建议在32bits - 256bits之间。      \\ \hline
	// rai_hready_o  & output  & 1    & AHB总线信号，该信号由总线内部产生，用于通知Master和Slave上次传输已经结束，总线已经准备就绪开始下一次传输。      \\ \hline
	// rai_hresp_o   & output  & 2    & AHB总线信号，Slave端设备的应答信号，表明当前传输是否成功，“1”错误。      \\ \hline
	// rai_intr_o    & output  & 1    & 中断控制信号      \\ \hline
  // r2t_clk_w     & output  & 1    & 信箱寄存器时钟 \\ \hline       
  // r2t_rst_n_w   & output  & 1    & 信箱寄存器复位信号，低电平有效      \\ \hline     
  // en_box        & output  & 1    & 信箱寄存器使能信号，高电平有效     \\ \hline 
  // write_box     & output  & 1    & 信箱寄存器读写控制信号，高电平为写，低电平为读     \\ \hline    
  // r2t_addr_w    & output  & 12   & 信箱寄存器地址信号     \\ \hline     
  // r2t_wdat_w    & output  & 32   & 信箱寄存器写数据信号     \\ \hline     
  // r2t_rdy_w     & input   & 1    & 信箱寄存器准备信号，高电平有效     \\ \hline    
  // r2t_rdat_w    & input   & 32   & 信箱寄存器读数据信号     \\ \hline     
  // r2t_intr_w    & input   & 1    & 信箱寄存器中断信号，高电平有效     \\ \hline     